// Treasury.cs
//
// Copyright (c) 2007 Mike Strobel
//
// This source code is subject to the terms of the Microsoft Reciprocal License (Ms-RL).
// For details, see <http://www.opensource.org/licenses/ms-rl.html>.
//
// All other rights reserved.

using System;
using System.Linq;

using Supremacy.Utility;

namespace Supremacy.Economy
{
    [Serializable]
    public class Treasury : ICloneable
    {
        private int _currentLevel;
        private int _grossIncome;
        private int _maintenance;
        private int _previousLevel;
        private int _previousChange;

        public Treasury() {}

        public Treasury(Treasury initialTreasury)
        {
            if (initialTreasury == null)
                throw new ArgumentNullException("initialTreasury");
            CopyFrom(initialTreasury);
        }
        
        public Treasury(int initialLevel)
        {
            if (initialLevel < 0)
                throw new ArgumentOutOfRangeException("initialLevel", "amount must be non-negative");
            this.CurrentLevel = initialLevel;
        }

        protected void CopyFrom(Treasury treasury)
        {
            if (treasury == null)
                throw new ArgumentNullException("treasury");
            this._currentLevel = treasury._currentLevel;
            this._grossIncome = treasury._grossIncome;
            this._maintenance = treasury._maintenance;
            this._previousChange = treasury._previousChange;
            this._previousLevel = treasury._previousLevel;
        }

        public int CurrentLevel
        {
            get { return _currentLevel; }
            protected set { _currentLevel = value; }
        }

        public int PreviousLevel
        {
            get { return _previousLevel; }
            protected set { _previousLevel = value; }
        }

        public int Maintenance
        {
            get { return _maintenance; }
            protected set { _maintenance = value; }
        }

        public int PreviousChange
        {
            get { return _previousChange; }
            protected set { _previousChange = value; }
        }

        public int CurrentChange
        {
            get { return (this.CurrentLevel - this.PreviousLevel); }
        }

        public int GrossIncome
        {
            get { return _grossIncome; }
            protected set
            {
                _grossIncome = value;
                Assert.IsNonNegative(_grossIncome);
            }
        }

        public bool IsBankruptcyImminent
        {
            get
            {
                if (this.CurrentChange >= 0)
                    return false;
                return (this.CurrentChange >= (this.PreviousChange * 3));
            }
        }

        public int NetIncome
        {
            get { return (this.GrossIncome - this.Maintenance); }
        }

        public bool TryGiveAmount(int amount)
        {
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount", "amount must be non-negative");
            if (amount > this.CurrentLevel)
                return false;
            this.CurrentLevel -= amount;
            return true;
        }

        public void Add(Treasury treasury)
        {
            if (treasury == null)
                throw new ArgumentNullException("treasury");
            Add(treasury.CurrentLevel);
        }

        public void Subtract(Treasury treasury)
        {
            if (treasury == null)
                throw new ArgumentNullException("treasury");
            Subtract(treasury.CurrentLevel);
        }

        public void Add(int amount)
        {
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount", "amount must be non-negative");
            this.CurrentLevel += amount;
        }

        public void Subtract(int amount)
        {
            if (amount < 0)
                amount *= -1;
            this.CurrentLevel -= amount;
        }

        public void AddIncome(int amount)
        {
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount", "amount must be non-negative");
            this.GrossIncome += amount;
            this.CurrentLevel += amount;
        }

        public void SubtractIncome(int amount)
        {
            if (amount < 0)
                amount *= -1;
            if (amount > this.CurrentLevel)
                amount = this.CurrentLevel;
            this.GrossIncome -= amount;
            this.CurrentLevel -= amount;
        }

        public void ClearIncome()
        {
            this.GrossIncome = 0;
        }

        public void AddMaintenance(int amount)
        {
            if (amount < 0)
                throw new ArgumentOutOfRangeException("amount", "amount must be non-negative");
            this.Maintenance += amount;
        }

        public void Update()
        {
            this.PreviousChange = this.CurrentChange;
            this.PreviousLevel = this.CurrentLevel;
            this.GrossIncome = 0;
            this.Maintenance = 0;
        }

        public void ClearAllValues()
        {
            this.CurrentLevel = 0;
            this.PreviousChange = 0;
            this.PreviousLevel = 0;
            this.GrossIncome = 0;
            this.Maintenance = 0;
        }

        public Treasury Clone()
        {
            return new Treasury(this);
        }

        #region ICloneable Members
        object ICloneable.Clone()
        {
            return Clone();
        }
        #endregion
    }
}